/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package pt.our.angemc.modifications;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import pt.our.angemc.datatypes.AlternativeSymbols;
import pt.our.angemc.datatypes.Grammar;
import pt.our.angemc.datatypes.NonTerminal;
import pt.our.angemc.datatypes.Production;
import pt.our.angemc.datatypes.Rule;
import pt.our.angemc.datatypes.Symbol;
import pt.our.angemc.datatypes.SymbolsSequence;

/**
 *
 * @author piskeiro
 */
public class RemoveUnitaryProductions extends GrammarModification{

    public RemoveUnitaryProductions(Grammar grammar){
        super(grammar);
    }

    private void substitute(HashMap<Symbol,ArrayList<Production>> unitaryMapping,ArrayList<Symbol> symbols,HashSet<Symbol> substitutionsMade){
        for(Symbol symbol : symbols){
            if(symbol instanceof NonTerminal){
                if(unitaryMapping.keySet().contains(symbol)){
                    int pos = symbols.indexOf(symbol);
                    symbols.set(pos, unitaryMapping.get(symbol).get(0).getSymbols().get(0));
                    substitutionsMade.add(symbol);
                }
            }else if(symbol instanceof AlternativeSymbols){
                substitute(unitaryMapping, ((AlternativeSymbols)symbol).getSymbols(),substitutionsMade);
            }else if(symbol instanceof SymbolsSequence){
                substitute(unitaryMapping, ((SymbolsSequence)symbol).getSymbols(),substitutionsMade);
            }//else - is a terminal -> does nothing
        }
    }


    @Override
    public void performModification() {
        if(!this.performedModification()){
            //perform modifications
            HashMap<Symbol,ArrayList<Production>> unitaryMapping=new HashMap<Symbol, ArrayList<Production>>();
            HashSet<Rule> unitaryProductions = this.getUnitaryProductionsSet();

            HashSet<Symbol> toDelete = new HashSet<Symbol>(); //
            HashSet<Symbol> substitutionsMade = new HashSet<Symbol>();


            for(Rule rule : unitaryProductions){
                unitaryMapping.put(rule.getLeftSymbol(), rule.getProductions());
            }
            for(Symbol s : unitaryMapping.keySet()){
                for(Symbol other : unitaryMapping.keySet()){
                    if(!s.equals(other) && s.getName().equals(unitaryMapping.get(other).get(0).getSymbols().get(0).getName())){
                        unitaryMapping.get(other).get(0).getSymbols().get(0).setName(unitaryMapping.get(s).get(0).getSymbols().get(0).getName());
                        toDelete.add(s);
                    }
                }
            }
            

            for(Rule rule : this.getRules()){
                if( !unitaryMapping.keySet().contains(rule.getLeftSymbol()) ){
                    for(Production production : rule.getProductions()){
                        //substitute(unitaryMapping,production.getSymbols(),substitutionsMade);
                        substitute(unitaryMapping,production.getSymbols(),substitutionsMade);
                    }
                }
            }
//            for(Symbol s : toDelete){
//                if(substitutionsMade.contains(s)){
//                    unitaryMapping.remove(s);
//                }
//            }
            for(Rule rule : unitaryProductions){
                if(substitutionsMade.contains(rule.getLeftSymbol()) || toDelete.contains(rule.getLeftSymbol())){
                    this.getRules().remove(rule);
                }
            }
            
            this.setPerformedModification(true);
            calculateModificationMetrics();
        }
    }

    @Override
    public HashMap<String,Object> predictModificationMetrics() {
        if(!predictedModificationMetrics() && !calculatedModificationMetrics()){
            HashMap<String,Object> metrics = this.getMetrics();
            if(metrics != null){
                int nonTerminals = (Integer)metrics.get(NON_TERMINALS_COUNT);
                int terminals = (Integer)metrics.get(TERMINALS_COUNT);
                int productions = (Integer)metrics.get(PRODUCTIONS_COUNT);
                int unitaryProductions = (Integer)metrics.get(UNITARY_PRODUCTIONS_COUNT);
                float rhsCount = (Float)metrics.get(RHS_COUNT);

                metrics.put(NON_TERMINALS_COUNT, nonTerminals-unitaryProductions);
                metrics.put(PRODUCTIONS_COUNT, productions-unitaryProductions);
                metrics.put(UNITARY_PRODUCTIONS_COUNT, 0);
    //            public final String RECURSIVE_COUNT             = "recursiveCount"; /*em principio nao se altera*/
                metrics.put(RHS_COUNT, (rhsCount*(1.0f*productions))/((productions-unitaryProductions)*1.0f));
                metrics.put(ALTERNATIVES_COUNT, ((productions-unitaryProductions)*1.0f)/((nonTerminals-unitaryProductions)*1.0f));
                metrics.put(RD_COUNT, terminals+(nonTerminals-unitaryProductions));
                metrics.put(LL1_COUNT, terminals*(nonTerminals-unitaryProductions));
            }
            setPredictedModificationMetrics(true);
        }
        return this.getMetrics();
    }

    @Override
    public HashMap<String,Object> calculateModificationMetrics() {
        if(!calculatedModificationMetrics()){
            this.calculateMetrics(true);
            setCalculatedModificationMetrics(true);
        }
        return this.getMetrics();
    }


}
